#pragma once
#include "atom.h"
#include "bond.h"
#include <unordered_map>

using arma::vec;
using arma::ivec;
using arma::mat;
using arma::imat;
using arma::umat;

#ifdef GRAPHENETOOLS_EXPORTS
#ifndef GRAPHENETOOLS_DLL_API
#define GRAPHENETOOLS_DLL_API __declspec(dllexport)
#endif
#else
#ifndef GRAPHENETOOLS_DLL_API
#define GRAPHENETOOLS_DLL_API __declspec(dllimport)
#endif
#endif

class GRAPHENETOOLS_DLL_API Unit
{
	Atom::list atoms;			// Container for atoms in this unit.
	Atom::sorter sort;			// Function obejct that sorts the atom list.
	Atom::filter filter;		// Function obejct that filters the atom list.
	Atom::reference::grid grid; // This contains the grid for the atoms.
	
	Bond::table bonds;		// Container for bonds in this unit.
	umat bondlocations;			// Container for bond locations as columns (orig,dest)-vectors.

	
	imat periodicity;			// Container for periodic vectors for this unit (as a sq matrix with the vectors as columns). Use getPeriodicDimensionality() to get the matrix order.
	imat offsets;			// Container for offset vectors for this unit.
	mat reciprocity;			// Container for reciprocal vectors for this unit (as a sq matrix with the vectors as columns). Use getPeriodicDimensionality() to get the matrix order.
	mat brillouin;				// Container for Brillouin Zone points for this unit (as a matrix with the point vectors as columns).
	
	bool initialized;			// This container holds weither the unit has been initialized.
	
public:	
	Unit(); // Defaut constructor returns an empty uninitialized unit.
	Unit(const Atom::list &alist, const imat &p = imat()); // Defaut constructor returns an uninitialized unit with atoms alist and periodicity p.
	Unit(const std::vector<ivec4> &alist, const std::vector<ivec4> &p = std::vector<ivec4>()); // Defaut constructor returns an uninitialized unit with atoms alist and periodicity p.
	
	void setAtoms(const Atom::list &alist);	// This setter function replaces the current atom list with alist. Note this resets the unit, see reset().

	const Atom::list &getAtoms() const;		// This getter function return a const reference to the atoms. Read only.
	const Bond::table &getBonds() const;// This getter function return a const reference to the atoms. Read only.
	const umat &getBondsLocations() const;// This getter function return a const reference to the atoms. Read only.
	
	bool isInitilized() const;			// This getter function returns weither the unit has been initialized.
	const imat &getPeriodicity() const; // This getter function returns the periodicity matrix.
	const mat &getReciprocity() const;	// This getter function returns the periodicity matrix.
	const mat &getBrillouin() const;	// This getter function returns the periodicity matrix.
	mat getPeriodicityXyz() const; // This getter function returns the periodicity matrix.

	// This setter function sets the periodicity, calculates the reciprocity and brillioun zone. Note this resets the unit, see reset().
	void setPeriodicity(const imat &p);
	// This setter function removes the currently set periodicity. Note this resets the unit if a current periodicity has been removed, see reset().
	void removePeriodicity();
	// This setter function calculates bonds according to the current Atom::list atoms.
	void initialize();
	// This setter function resets the initalization (clears bonds);
	void reset();

	static imat recursive_find_p_vectors (const imat &per);

protected:
	static mat toXyz(const imat &m);	// Convenience function to transform from lattice coordinates (imat) to real space coordinates (mat).
	static mat basisChangeMat();		// Convenience function initiate the basis change matrix in toXyz().
	void setReciprocity();				// Convenience function that sets brillouin matrix after setting periodicity.
	void setBrillouin();				// Convenience function that sets brillouin matrix after setting reciprocity.
	void setBonds();				// Convenience function that sets brillouin matrix after setting reciprocity.
};

